<!DOCTYPE html>
<html>
<head hexo-theme='https://volantis.js.org/#2.6.6'>
  <meta charset="utf-8">
  <!-- SEO相关 -->
  
    
  
  <!-- 渲染优化 -->
  <meta name="renderer" content="webkit">
  <meta name="force-rendering" content="webkit">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="HandheldFriendly" content="True" >
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  <!-- 页面元数据 -->
  
    <title>Spring-Boot|@AutoConfigurationPackage的研究 - enhao&#39;s Blog|个人博客</title>
  
    <meta name="keywords" content="Spring-Boot,EnableAutoConfiguration,AutoConfigurationPackage">
  
  
    <meta name="description" content="
代码来自于《Spring Boot编程思想（核心篇）》的示例工程thinking-in-spring-boot:first-app-by-gui，并在此基础上修改。

最近在看《Spring Boot编程思想（核心篇）》时，在理解自动装配章节讨论到了@SpringBootApplication注解。
@Spri...">
  

  <!-- feed -->
  
    <link rel="alternate" href="/atom.xml" title="enhao's Blog|个人博客">
  

  <!-- import meta -->
  

  <!-- link -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.13/css/all.min.css">
  
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css">

  
  
    
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-waves@0.7.6/dist/waves.min.css">

  

  

  

  

  <!-- import link -->
  

  
  
    
<link rel="stylesheet" href="/css/style.css">

  

  <script>
    function setLoadingBarProgress(num) {
      document.getElementById('loading-bar').style.width=num+"%";
    }
  </script>

  
  
</head>

<body>
  
  <div id="loading-bar-wrapper">
  <div id="loading-bar"></div>
</div>
<header class="l_header shadow blur">
  <div class='container'>
  <div class='wrapper'>
    <div class='nav-sub'>
      <p class="title"></p>
      <ul class='switcher nav-list-h'>
        <li><a class="s-comment fas fa-comments fa-fw" target="_self" href='javascript:void(0)'></a></li>
        
          <li><a class="s-toc fas fa-list fa-fw" target="_self" href='javascript:void(0)'></a></li>
        
      </ul>
    </div>
		<div class="nav-main">
      
        
        <a class="title flat-box" target="_self" href='/'>
          
          
          
            enhao's Blog
          
          
        </a>
      

			<div class='menu navigation'>
				<ul class='nav-list-h'>
          
          
          
            
            
              <li>
                <a class="flat-box" href=/
                  
                  
                  
                    id="home"
                  >
                  <i class='fas fa-book fa-fw'></i>博客
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="flat-box" href=/categories/
                  
                  
                  
                    id="categories"
                  >
                  <i class='fas fa-folder-open fa-fw'></i>分类
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="flat-box" href=/tags/
                  
                  
                  
                    id="tags"
                  >
                  <i class='fas fa-tags fa-fw'></i>标签
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="flat-box" href=/archives/
                  
                  
                  
                    id="archives"
                  >
                  <i class='fas fa-archive fa-fw'></i>归档
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="flat-box" href=/friends/
                  
                  
                  
                    id="friends"
                  >
                  <i class='fas fa-link fa-fw'></i>友链
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="flat-box" href=/about/
                  
                  
                  
                    id="about"
                  >
                  <i class='fas fa-info-circle fa-fw'></i>关于
                </a>
                
              </li>
            
          
          
				</ul>
			</div>

      <div class="m_search">
        <form name="searchform" class="form u-search-form">
          <i class="icon fas fa-search fa-fw"></i>
          <input type="text" class="input u-search-input" placeholder="Search..." />
        </form>
      </div>

			<ul class='switcher nav-list-h'>
				
					<li><a class="s-search fas fa-search fa-fw" target="_self" href='javascript:void(0)'></a></li>
				
				<li>
          <a class="s-menu fas fa-bars fa-fw" target="_self" href='javascript:void(0)'></a>
          <ul class="menu-phone list-v navigation white-box">
            
              
            
              <li>
                <a class="flat-box" href=/
                  
                  
                  
                    id="home"
                  >
                  <i class='fas fa-book fa-fw'></i>博客
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="flat-box" href=/categories/
                  
                  
                  
                    id="categories"
                  >
                  <i class='fas fa-folder-open fa-fw'></i>分类
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="flat-box" href=/tags/
                  
                  
                  
                    id="tags"
                  >
                  <i class='fas fa-tags fa-fw'></i>标签
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="flat-box" href=/archives/
                  
                  
                  
                    id="archives"
                  >
                  <i class='fas fa-archive fa-fw'></i>归档
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="flat-box" href=/friends/
                  
                  
                  
                    id="friends"
                  >
                  <i class='fas fa-link fa-fw'></i>友链
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="flat-box" href=/about/
                  
                  
                  
                    id="about"
                  >
                  <i class='fas fa-info-circle fa-fw'></i>关于
                </a>
                
              </li>
            
          
            
          </ul>
        </li>
			</ul>
		</div>
	</div>
  </div>
</header>

<script>setLoadingBarProgress(40);</script>



  <div class="l_body nocover">
    <div class='body-wrapper'>
      

<div class='l_main'>
  

  
    <article id="post" class="post white-box reveal shadow article-type-post" itemscope itemprop="blogPost">
      


  <section class='meta'>
    
      
      
      <div class="meta" id="header-meta">
        
          
  <h1 class="title">
    <a href="/2020/05/23/Spring-Boot%E4%B8%AD@AutoConfigurationPackage%E7%9A%84%E7%A0%94%E7%A9%B6/">
      Spring-Boot|@AutoConfigurationPackage的研究
    </a>
  </h1>


        
        <div class='new-meta-box'>
          
            
          
            
              
<div class='new-meta-item author'>
  <a href="https://liangenhao.gitee.io" rel="nofollow">
    <img src="https://portrait.gitee.com/uploads/avatars/user/509/1528373_liangenhao_1590301253.png">
    <p>enhao</p>
  </a>
</div>

            
          
            
              
  
  <div class='new-meta-item category'>
    <a href='/categories/Spring-Boot/' rel="nofollow">
      <i class="fas fa-folder-open fa-fw" aria-hidden="true"></i>
      <p>读书笔记/Spring-Boot</p>
    </a>
  </div>


            
          
            
              <div class="new-meta-item date">
  <a class='notlink'>
    <i class="fas fa-calendar-alt fa-fw" aria-hidden="true"></i>
    <p>发布于：2020年5月23日</p>
  </a>
</div>

            
          
            
              

            
          
        </div>
        
          <hr>
        
      </div>
    
  </section>


      <section class="article typo">
        <div class="article-entry" itemprop="articleBody">
          
          <blockquote>
<p>代码来自于《Spring Boot编程思想（核心篇）》的示例工程<code>thinking-in-spring-boot:first-app-by-gui</code>，并在此基础上修改。</p>
</blockquote>
<p>最近在看《Spring Boot编程思想（核心篇）》时，在<code>理解自动装配</code>章节讨论到了<code>@SpringBootApplication</code>注解。</p>
<p><code>@SpringBootApplication</code>是一个组合注解，等价于<code>@EnableAutoConfiguration</code>、<code>@ComponentScan</code>、<code>@SpringBootConfiguration</code>。</p>
<blockquote>
<p>基于<code>Spring Boot 2.0.2.RELEASE</code>中。</p>
</blockquote>
<a id="more"></a>

<p>关于其中的<code>@EnableAutoConfiguration</code>注解，同样是一个组合注解，等价于<code>@AutoConfigurationPackage</code>和<code>@Import(EnableAutoConfigurationImportSelector.class)</code>。</p>
<p>之前看到过一些文章讨论过这个注解的作用是开启自动配置。其中的<code>@AutoConfigurationPackage</code>注解是用作自动配置包，将主配置类(<code>@SpringBootApplication</code>标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器中。</p>
<p>对于这句话产生了很多疑惑：</p>
<ol>
<li>这个”自动配置包”的描述比较模糊。</li>
<li><code>@AutoConfigurationPackage</code>是如何将主配置类的所在包及下面子包里所有组件扫描到容器中的？</li>
<li>按着这个描述，<code>@AutoConfigurationPackage</code>和另一个注解<code>@ComponentScan</code>又有什么区别？</li>
</ol>
<p>带着这个疑惑，发现大部分文章里都是千篇一律的这种说法，并没有详细解答。</p>
<h2 id="猜想和尝试"><a href="#猜想和尝试" class="headerlink" title="猜想和尝试"></a>猜想和尝试</h2><p>上面说<code>@AutoConfigurationPackage</code>的作用是扫描标注了该类的所在包及其下面所有子包里面的所有组件。个人感觉和<code>@ComponentScan</code>注解有点重复，因此做一个尝试：</p>
<p>【步骤一】：编写主配置类，不使用<code>@SpringBootApplication</code>注解标注，改用<code>@EnableAutoConfiguration</code>注解。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> thinking.in.spring.boot.firstappbygui;</span><br><span class="line"></span><br><span class="line"><span class="meta">@EnableAutoConfiguration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">FirstAppByGuiApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">    SpringApplication.run(FirstAppByGuiApplication<span class="class">.<span class="keyword">class</span>, <span class="title">args</span>)</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>这样做是想尝试下是不是<code>@AutoConfigurationPackage</code>做了和<code>@ComponentScan</code>一样的工作。</p>
</blockquote>
<p>【步骤二】：在主配置类<code>FirstAppByGuiApplication</code>所在包下，创建一个配置类。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> thinking.in.spring.boot.firstappbygui.conf;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WebConfiguration</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Bean</span></span><br><span class="line">  <span class="function"><span class="keyword">public</span> RouterFunction&lt;ServerResponse&gt; <span class="title">helloWorld</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> route(GET(<span class="string">"/hello-world"</span>),</span><br><span class="line">                 request -&gt; ok().body(Mono.just(<span class="string">"Hello,World"</span>), String<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">                )</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>【步骤三】：启动应用，并访问：<code>http://localhost:8080/hello-world</code>地址，发现是404。</p>
<p>说明<code>WebConfiguration</code> Bean没有被扫描到Spring容器中。这和上面说的结论就相悖了。</p>
<p>【修改步骤一】：添加<code>@ComponentScan</code>注解。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> thinking.in.spring.boot.firstappbygui;</span><br><span class="line"></span><br><span class="line"><span class="meta">@EnableAutoConfiguration</span></span><br><span class="line"><span class="meta">@ComponentScan</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">FirstAppByGuiApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">    SpringApplication.run(FirstAppByGuiApplication<span class="class">.<span class="keyword">class</span>, <span class="title">args</span>)</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>其他配置不变，启动应用，再次访问<code>http://localhost:8080/hello-world</code>地址，发现可以访问成功。</p>
<p>到这里，可以知道，将<u>主配置类的所在包及下面所有子包里面的所有组件扫描到Spring容器中</u>这个活是由<code>@ComponentScan</code>来做的。</p>
<p>那么<code>@EnableAutoConfiguration</code>中的<code>@AutoConfigurationPackage</code>到底做了什么事呢？</p>
<h2 id="AutoConfigurationPackages-Registrar"><a href="#AutoConfigurationPackages-Registrar" class="headerlink" title="AutoConfigurationPackages.Registrar"></a>AutoConfigurationPackages.Registrar</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target</span>(ElementType.TYPE)</span><br><span class="line"><span class="meta">@Retention</span>(RetentionPolicy.RUNTIME)</span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Inherited</span></span><br><span class="line"><span class="meta">@Import</span>(AutoConfigurationPackages.Registrar<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class"><span class="title">public</span> @<span class="title">interface</span> <span class="title">AutoConfigurationPackage</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>@AutoConfigurationPackage</code>注解使用<code>@Import</code>引入了<code>AutoConfigurationPackages.Registrar</code>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// org.springframework.boot.autoconfigure.AutoConfigurationPackages</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AutoConfigurationPackages</span> </span>&#123;</span><br><span class="line">  <span class="comment">// 注册当前标注类所在package的Bean的名称</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String BEAN = AutoConfigurationPackages<span class="class">.<span class="keyword">class</span>.<span class="title">getName</span>()</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 注册当前标注类所在package为Spring Bean</span></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">register</span><span class="params">(BeanDefinitionRegistry registry, String... packageNames)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 如果容器中已存在这个Bean，则修改Bean的属性</span></span><br><span class="line">		<span class="keyword">if</span> (registry.containsBeanDefinition(BEAN)) &#123;</span><br><span class="line">			BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);</span><br><span class="line">			ConstructorArgumentValues constructorArguments = beanDefinition</span><br><span class="line">					.getConstructorArgumentValues();</span><br><span class="line">      <span class="comment">// 修改beanDefinition的构造参数元信息，将本次的标注类所在的package追加到Bean中</span></span><br><span class="line">			constructorArguments.addIndexedArgumentValue(<span class="number">0</span>,</span><br><span class="line">					addBasePackages(constructorArguments, packageNames));</span><br><span class="line">		&#125;</span><br><span class="line">    <span class="comment">// 如果容器中不存在这个Bean，则创建一个Bean，并注册到容器中</span></span><br><span class="line">		<span class="keyword">else</span> &#123;</span><br><span class="line">			GenericBeanDefinition beanDefinition = <span class="keyword">new</span> GenericBeanDefinition();</span><br><span class="line">			beanDefinition.setBeanClass(BasePackages<span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line">			beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(<span class="number">0</span>,</span><br><span class="line">					packageNames);</span><br><span class="line">			beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);</span><br><span class="line">			registry.registerBeanDefinition(BEAN, beanDefinition);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> String[] addBasePackages(</span><br><span class="line">			ConstructorArgumentValues constructorArguments, String[] packageNames) &#123;</span><br><span class="line">		String[] existing = (String[]) constructorArguments</span><br><span class="line">				.getIndexedArgumentValue(<span class="number">0</span>, String[]<span class="class">.<span class="keyword">class</span>).<span class="title">getValue</span>()</span>;</span><br><span class="line">		Set&lt;String&gt; merged = <span class="keyword">new</span> LinkedHashSet&lt;&gt;();</span><br><span class="line">		merged.addAll(Arrays.asList(existing));</span><br><span class="line">		merged.addAll(Arrays.asList(packageNames));</span><br><span class="line">		<span class="keyword">return</span> StringUtils.toStringArray(merged);</span><br><span class="line">	&#125;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar</span></span><br><span class="line">  <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Registrar</span> <span class="keyword">implements</span> <span class="title">ImportBeanDefinitionRegistrar</span>, <span class="title">DeterminableImports</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">		<span class="meta">@Override</span></span><br><span class="line">		<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">registerBeanDefinitions</span><span class="params">(AnnotationMetadata metadata,</span></span></span><br><span class="line"><span class="function"><span class="params">				BeanDefinitionRegistry registry)</span> </span>&#123;</span><br><span class="line">      <span class="comment">// &lt;1&gt; PackageImport的作用是用来获取包名的，这里的包名就是标注了@EnableAutoConfiguration注解的类所在的包名：thinking.in.spring.boot.firstappbygui</span></span><br><span class="line">      <span class="comment">// 调用register方法，是想将当前标注类所在package注册到Spring容器中</span></span><br><span class="line">			register(registry, <span class="keyword">new</span> PackageImport(metadata).getPackageName());</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="meta">@Override</span></span><br><span class="line">		<span class="function"><span class="keyword">public</span> Set&lt;Object&gt; <span class="title">determineImports</span><span class="params">(AnnotationMetadata metadata)</span> </span>&#123;</span><br><span class="line">			<span class="keyword">return</span> Collections.singleton(<span class="keyword">new</span> PackageImport(metadata));</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>AutoConfigurationPackages.Registrar</code>类作用是将当前标注了<code>@AutoConfigurationPackage</code>的类所在的package注册到Spring容器中。</p>
<p>因为<code>@AutoConfigurationPackage</code>或者<code>@EnableAutoConfiguration</code>可能被不同的配置Class标注，因此<code>AutoConfigurationPackages.Registrar</code>可能被多次Import。因此当容器中已经存在该Bean后，后续的<code>@Import</code>将调整<code>BeanDefinition</code>的构造器参数元信息，添加当前标注类所在package到已有集合中。</p>
<blockquote>
<p>内容来自《Spring Boot编程思想（核心篇）》第9章 Spring Boot自动装配 9.2.7 @EnableAutoConfiguration自动装配BasePackages。</p>
</blockquote>
<p>对于之前的案例，是将<code>thinking.in.spring.boot.firstappbygui</code>包名注册到Spring容器中（注册为一个Spring Bean），Bean的名称为<code>org.springframework.boot.autoconfigure.AutoConfigurationPackages</code>。</p>
<p>那么，将标注类所在的package注册到Spring容器中的作用是什么呢？</p>
<h2 id="AutoConfigurationPackage的作用"><a href="#AutoConfigurationPackage的作用" class="headerlink" title="@AutoConfigurationPackage的作用"></a>@AutoConfigurationPackage的作用</h2><p>首先看下<code>@EnableAutoConfiguration</code>的部分代码注释：</p>
<blockquote>
<p>The package of the class that is annotated with @EnableAutoConfiguration, usually via @SpringBootApplication, has specific significance and is often used as a ‘default’. For example, it will be used when scanning for @Entity classes. It is generally recommended that you place @EnableAutoConfiguration (if you’re not using @SpringBootApplication) in a root package so that all sub-packages and classes can be searched.</p>
</blockquote>
<p>标注了<code>@EnableAutoConfiguration</code>类的包通常具有特定的意义。例如，在扫描标注了<code>@Entity</code>类时会被使用到。因此，推荐将<code>@EnableAutoConfiguration</code>放在根包中，这样所有的子包和类都可以被搜索到。</p>
<p>再看下<code>AutoConfigurationPackages</code>类的代码注释：</p>
<blockquote>
<p>Class for storing auto-configuration packages for reference later (e.g. by JPA entity scanner).</p>
</blockquote>
<p>用于存储自动配置程序包以供以后参考的类（例如，由JPA实体扫描程序提供）。</p>
<p>到此，可以知道<code>@AutoConfigurationPackage</code>不是用来扫描标注了<code>@Component</code>、<code>@Configuration</code>、<code>@Service</code>这些类的。而是用来扫描标注了例如<code>@Entity</code>类的。那么它是怎么工作的呢？</p>
<p>【<code>AutoConfigurationPackages</code>的工具方法】：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AutoConfigurationPackages</span> </span>&#123;</span><br><span class="line">  <span class="comment">// 注册当前标注类所在package的Bean的名称</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String BEAN = AutoConfigurationPackages<span class="class">.<span class="keyword">class</span>.<span class="title">getName</span>()</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 判断对于指定的容器中是否存在当前标注类所在package的Bean</span></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">has</span><span class="params">(BeanFactory beanFactory)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> beanFactory.containsBean(BEAN) &amp;&amp; !get(beanFactory).isEmpty();</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 获取指定容器中的当前标注类所在package的Bean</span></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> List&lt;String&gt; <span class="title">get</span><span class="params">(BeanFactory beanFactory)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> beanFactory.getBean(BEAN, BasePackages<span class="class">.<span class="keyword">class</span>).<span class="title">get</span>()</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">catch</span> (NoSuchBeanDefinitionException ex) &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(</span><br><span class="line">        <span class="string">"Unable to retrieve @EnableAutoConfiguration base packages"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>当某些组件或模块，需要进行包扫描时，就可以通过<code>AutoConfigurationPackages#get</code>方法，获取标注类所在的package来进行包扫描，从而实现”自动配置包”：</p>
<p>【在JPA中的应用】：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@EnableConfigurationProperties</span>(JpaProperties<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">Import</span>(<span class="title">DataSourceInitializedPublisher</span>.<span class="title">Registrar</span>.<span class="title">class</span>)</span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">abstract</span> <span class="title">class</span> <span class="title">JpaBaseConfiguration</span> <span class="keyword">implements</span> <span class="title">BeanFactoryAware</span> </span>&#123;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">protected</span> String[] getPackagesToScan() &#123;</span><br><span class="line">		List&lt;String&gt; packages = EntityScanPackages.get(<span class="keyword">this</span>.beanFactory)</span><br><span class="line">				.getPackageNames();</span><br><span class="line">		<span class="keyword">if</span> (packages.isEmpty() &amp;&amp; AutoConfigurationPackages.has(<span class="keyword">this</span>.beanFactory)) &#123;</span><br><span class="line">			packages = AutoConfigurationPackages.get(<span class="keyword">this</span>.beanFactory);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> packages.toArray(<span class="keyword">new</span> String[packages.size()]);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>JPA的自动配置扫描的包就是标注了<code>@EnableAutoConfiguration</code>类所在的package，通常是我们的主配置类。</p>
</blockquote>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul>
<li>《Spring Boot编程思想（核心篇）》</li>
<li><a href="https://docs.spring.io/spring-boot/docs/2.0.2.RELEASE/api/" target="_blank" rel="noopener">Spring Boot官方API</a></li>
</ul>

          
            <div class='article_footer'>
              
                
  
    
    



  

  
    
    



  

  
    
    

<section class="widget copyright  desktop mobile">
  <div class='content'>
    
      <blockquote>
        
          
            <p>博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议</p>

          
        
          
            <p>本文永久链接是：<a href=https://liangenhao.gitee.io/2020/05/23/Spring-Boot%E4%B8%AD@AutoConfigurationPackage%E7%9A%84%E7%A0%94%E7%A9%B6/>https://liangenhao.gitee.io/2020/05/23/Spring-Boot%E4%B8%AD@AutoConfigurationPackage%E7%9A%84%E7%A0%94%E7%A9%B6/</a></p>
          
        
      </blockquote>
    
  </div>
</section>

  

  
    
    

<section class="widget qrcode  ">
  

  <div class='content article-entry'>
    
      
        <div class='fancybox'><img src='https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/qrcode/wiki_volantis.png'
        
          height='64px'
        ></div>
      
    
      
        <div class='fancybox'><img src='https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/qrcode/wiki_volantis.png'
        
          height='64px'
        ></div>
      
    
  </div>
</section>

  


              
            </div>
          
        </div>
        
          


  <section class='meta' id="footer-meta">
    <div class='new-meta-box'>
      
        
          <div class="new-meta-item date" itemprop="dateUpdated" datetime="2020-05-24T00:00:00+08:00">
  <a class='notlink'>
    <i class="fas fa-edit fa-fw" aria-hidden="true"></i>
    <p>更新于：2020年5月24日</p>
  </a>
</div>

        
      
        
          
  
  <div class="new-meta-item meta-tags"><a class="tag" href="/tags/Spring-Boot/" rel="nofollow"><i class="fas fa-hashtag fa-fw" aria-hidden="true"></i><p>Spring-Boot</p></a></div> <div class="new-meta-item meta-tags"><a class="tag" href="/tags/EnableAutoConfiguration/" rel="nofollow"><i class="fas fa-hashtag fa-fw" aria-hidden="true"></i><p>EnableAutoConfiguration</p></a></div> <div class="new-meta-item meta-tags"><a class="tag" href="/tags/AutoConfigurationPackage/" rel="nofollow"><i class="fas fa-hashtag fa-fw" aria-hidden="true"></i><p>AutoConfigurationPackage</p></a></div>


        
      
        
          

        
      
        
          
  <div class="new-meta-item share -mob-share-list">
  <div class="-mob-share-list share-body">
    
      
        <a class="-mob-share-qq" title="" rel="external nofollow noopener noreferrer"
          
          href="http://connect.qq.com/widget/shareqq/index.html?url=https://liangenhao.gitee.io/2020/05/23/Spring-Boot%E4%B8%AD@AutoConfigurationPackage%E7%9A%84%E7%A0%94%E7%A9%B6/&title=Spring-Boot|@AutoConfigurationPackage的研究 - enhao's Blog|个人博客&summary=
代码来自于《Spring Boot编程思想（核心篇）》的示例工程thinking-in-spring-boot:first-app-by-gui，并在此基础上修改。

最近在看《Spring Boot编程思想（核心篇）》时，在理解自动装配章节讨论到了@SpringBootApplication注解。
@SpringBootApplication是一个组合注解，等价于@EnableAutoConfiguration、@ComponentScan、@SpringBootConfiguration。

基于Spring Boot 2.0.2.RELEASE中。
"
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/logo/128/qq.png">
          
        </a>
      
    
      
        <a class="-mob-share-qzone" title="" rel="external nofollow noopener noreferrer"
          
          href="https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=https://liangenhao.gitee.io/2020/05/23/Spring-Boot%E4%B8%AD@AutoConfigurationPackage%E7%9A%84%E7%A0%94%E7%A9%B6/&title=Spring-Boot|@AutoConfigurationPackage的研究 - enhao's Blog|个人博客&summary=
代码来自于《Spring Boot编程思想（核心篇）》的示例工程thinking-in-spring-boot:first-app-by-gui，并在此基础上修改。

最近在看《Spring Boot编程思想（核心篇）》时，在理解自动装配章节讨论到了@SpringBootApplication注解。
@SpringBootApplication是一个组合注解，等价于@EnableAutoConfiguration、@ComponentScan、@SpringBootConfiguration。

基于Spring Boot 2.0.2.RELEASE中。
"
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/logo/128/qzone.png">
          
        </a>
      
    
      
        <a class="-mob-share-weibo" title="" rel="external nofollow noopener noreferrer"
          
          href="http://service.weibo.com/share/share.php?url=https://liangenhao.gitee.io/2020/05/23/Spring-Boot%E4%B8%AD@AutoConfigurationPackage%E7%9A%84%E7%A0%94%E7%A9%B6/&title=Spring-Boot|@AutoConfigurationPackage的研究 - enhao's Blog|个人博客&summary=
代码来自于《Spring Boot编程思想（核心篇）》的示例工程thinking-in-spring-boot:first-app-by-gui，并在此基础上修改。

最近在看《Spring Boot编程思想（核心篇）》时，在理解自动装配章节讨论到了@SpringBootApplication注解。
@SpringBootApplication是一个组合注解，等价于@EnableAutoConfiguration、@ComponentScan、@SpringBootConfiguration。

基于Spring Boot 2.0.2.RELEASE中。
"
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/logo/128/weibo.png">
          
        </a>
      
    
  </div>
</div>



        
      
    </div>
  </section>


        
        
          <div class="prev-next">
            
              <a class='prev' href='/2021/02/08/redisTemplate%E6%80%A7%E8%83%BD%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5/'>
                <p class='title'><i class="fas fa-chevron-left" aria-hidden="true"></i>Redis|redisTemplate性能问题排查</p>
                <p class='content'>环境信息
JDK版本：1.8.0_191

Spring Boot版本：1.5.7.RELEASE

spring-data-redis版本：1.8.7.RELEASE

spring-data...</p>
              </a>
            
            
              <a class='next' href='/2019/11/16/%E7%8E%B0%E7%BD%91OOM%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5/'>
                <p class='title'>Java|现网OOM问题排查<i class="fas fa-chevron-right" aria-hidden="true"></i></p>
                <p class='content'>问题业务场景描述【发生OutOfMemoryError的业务场景】：

首先要将用户制作的内容l转成图片。
然后根据业务需要，以第1步中的图片为基础，再生成多张图片。
将所有生成的图片打成压缩包...</p>
              </a>
            
          </div>
        
      </section>
    </article>
  

  
    <!-- 显示推荐文章和评论 -->



  <article class="post white-box reveal comments shadow">
    <section class="article typo">
      <p ct><i class='fas fa-comments'></i> 评论</p>
      
      
      
      
      
      
        <section id="comments">
          <div id="valine_container" class="valine_thread">
            <i class="fas fa-cog fa-spin fa-fw fa-2x"></i>
          </div>
        </section>
      
      
    </section>
  </article>


  




<!-- 根据页面mathjax变量决定是否加载MathJax数学公式js -->



  <script>
    window.subData = {
      title: 'Spring-Boot|@AutoConfigurationPackage的研究',
      tools: true
    }
  </script>


</div>
<aside class='l_side'>
  
  

  
    
    


  <section class="widget toc-wrapper shadow desktop mobile">
    
  <header>
    
      <i class="fas fa-list fa-fw" aria-hidden="true"></i><span class='name'>本文目录</span>
    
  </header>


    <div class='content'>
      <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#猜想和尝试"><span class="toc-text">猜想和尝试</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#AutoConfigurationPackages-Registrar"><span class="toc-text">AutoConfigurationPackages.Registrar</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#AutoConfigurationPackage的作用"><span class="toc-text">@AutoConfigurationPackage的作用</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#参考资料"><span class="toc-text">参考资料</span></a></li></ol>
    </div>
  </section>


  


</aside>


  
  <footer class="clearfix">
    <br><br>
    
      
        <div class="aplayer-container">
          


        </div>
      
    
      
        <br>
        <div class="social-wrapper">
          
            
              <a href="/atom.xml"
                class="social fas fa-rss flat-btn"
                target="_blank"
                rel="external nofollow noopener noreferrer">
              </a>
            
          
            
              <a href="mailto:liangenhao@hotmail.com"
                class="social fas fa-envelope flat-btn"
                target="_blank"
                rel="external nofollow noopener noreferrer">
              </a>
            
          
            
              <a href="https://github.com/liangenhao"
                class="social fab fa-github flat-btn"
                target="_blank"
                rel="external nofollow noopener noreferrer">
              </a>
            
          
            
              <a href="https://weibo.com/u/2165970150"
                class="social fab fa-weibo flat-btn"
                target="_blank"
                rel="external nofollow noopener noreferrer">
              </a>
            
          
            
              <a href="https://music.163.com/#/user/home?id=261817684"
                class="social fas fa-headphones-alt flat-btn"
                target="_blank"
                rel="external nofollow noopener noreferrer">
              </a>
            
          
        </div>
      
    
      
        <div><p>博客内容遵循 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" target="_blank" rel="noopener">署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议</a></p>
</div>
      
    
      
        本站使用
        <a href="https://volantis.js.org/" target="_blank" class="codename">Volantis</a>
        作为主题，总访问量为
          <span id="busuanzi_value_site_pv"><i class="fas fa-circle-notch fa-spin fa-fw" aria-hidden="true"></i></span>
          次
        
      
    
      
        <div class='copyright'>
        <p><a href="https://liangenhao.gitee.io">Copyright © 2017-2020 enhao</a></p>

        </div>
      
    
  </footer>

<script>setLoadingBarProgress(80);</script>


      <script>setLoadingBarProgress(60);</script>
    </div>
    <a class="s-top fas fa-arrow-up fa-fw" href='javascript:void(0)'></a>
  </div>
  
<script src="https://cdn.jsdelivr.net/npm/jquery@3.4/dist/jquery.min.js"></script>


  <script>
    
    var SEARCH_SERVICE = "hexo" || "hexo";
    var ROOT = "/" || "/";
    if (!ROOT.endsWith('/')) ROOT += '/';
  </script>


  <script async src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-volantis@2/js/instant_page.js" type="module" defer integrity="sha384-OeDn4XE77tdHo8pGtE1apMPmAipjoxUQ++eeJa6EtJCfHlvijigWiJpD7VDPWXV1"></script>


  <script src="https://cdn.jsdelivr.net/npm/scrollreveal@4.0.6/dist/scrollreveal.min.js"></script>
  <script type="text/javascript">
    $(function() {
      ScrollReveal().reveal('.l_main .reveal', {
        distance: '8px',
        duration: '800',
        interval: '100',
        scale: '1'
      });
    });
  </script>


  
<script src="https://cdn.jsdelivr.net/npm/node-waves@0.7.6/dist/waves.min.js"></script>

  <script type="text/javascript">
    $(function() {
      Waves.attach('.flat-btn', ['waves-button']);
      Waves.attach('.float-btn', ['waves-button', 'waves-float']);
      Waves.attach('.float-btn-light', ['waves-button', 'waves-float', 'waves-light']);
      Waves.attach('.flat-box', ['waves-block']);
      Waves.attach('.float-box', ['waves-block', 'waves-float']);
      Waves.attach('.waves-image');
      Waves.init();
    });
  </script>


  <script defer src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-busuanzi@2.3/js/busuanzi.pure.mini.js"></script>



  
  
  
    
<script src="https://cdn.jsdelivr.net/npm/jquery-backstretch@2.1.18/jquery.backstretch.min.js"></script>

    <script type="text/javascript">
      $(function(){
        var imgs=["https://cdn.jsdelivr.net/gh/xaoxuu/cdn-wallpaper/abstract/BBC19066-E176-47C2-9D22-48C81EE5DF6B.jpeg"];
        if ('true' == 'true') {
          function shuffle(arr){
            /*From countercurrent-time*/
            var n = arr.length;
            while(n--) {
              var index = Math.floor(Math.random() * n);
              var temp = arr[index];
              arr[index] = arr[n];
              arr[n] = temp;
            }
          }
          shuffle(imgs);
        }
        if ('') {
          $('').backstretch(
            imgs,
          {
            duration: "20000",
            fade: "1500"
          });
        } else {
          $.backstretch(
            imgs,
          {
            duration: "20000",
            fade: "1500"
          });
        }
      });
    </script>
  











  
    
<script src="https://cdn.jsdelivr.net/npm/valine@1.4/dist/Valine.min.js"></script>

  
  <script>
  var GUEST_INFO = ['nick','mail','link'];
  var meta = 'nick,mail,link'.split(',').filter(function(item){
    return GUEST_INFO.indexOf(item) > -1
  });
  var REQUIRED_FIELDS = ['nick','mail','link'];
  var requiredFields = 'nick,mail'.split(',').filter(function(item){
    return REQUIRED_FIELDS.indexOf(item) > -1
  });
  var valine = new Valine();
  function emoji(path, idx, ext) {
      return path + "/" + path + "-" + idx + "." + ext;
  }
  var emojiMaps = {};
  for (var i = 1; i <= 54; i++) {
    emojiMaps['tieba-' + i] = emoji('tieba', i, 'png');
  }
  for (var i = 1; i <= 101; i++) {
    emojiMaps['qq-' + i] = emoji('qq', i, 'gif');
  }
  for (var i = 1; i <= 116; i++) {
    emojiMaps['aru-' + i] = emoji('aru', i, 'gif');
  }
  for (var i = 1; i <= 125; i++) {
    emojiMaps['twemoji-' + i] = emoji('twemoji', i, 'png');
  }
  for (var i = 1; i <= 4; i++) {
    emojiMaps['weibo-' + i] = emoji('weibo', i, 'png');
  }
  valine.init({
    el: '#valine_container',
    meta: meta,
    
    appId: "6HU1Hs9NgLvqp3IreajdKnai-gzGzoHsz",
    appKey: "6dtTgWazUc7nT1S5uOn7FgNa",
    placeholder: "快来评论吧~",
    pageSize:'10',
    avatar:'robohash',
    lang:'zh-cn',
    visitor: 'true',
    highlight: 'true',
    mathJax: 'false',
    enableQQ: 'true',
    requiredFields: requiredFields,
    emojiCDN: 'https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/emoji/valine/',
    emojiMaps: emojiMaps
  })
  </script>





  
<script src="/js/app.js"></script>



  
<script src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-volantis@2.6.5/js/search.js"></script>



  
<script src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-volantis@2/js/comment_typing.js"></script>






<!-- 复制 -->

  <script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="fas fa-copy"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('fa-copy');
        $icon.addClass('fa-check-circle');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('fa-check-circle');
          $icon.addClass('fa-copy');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('fa-copy');
        $icon.addClass('fa-times-circle');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('fa-times-circle');
          $icon.addClass('fa-copy');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>




<!-- fancybox -->
<script src="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js"></script>
<script>
  function pjax_fancybox() {
    $(".article-entry").find("img").not('.inline').not('a img').each(function () { //渲染 fancybox
      var element = document.createElement("a"); // a 标签
      $(element).attr("pjax-fancybox", "");  // 过滤 pjax
      $(element).attr("href", $(this).attr("src"));
      if ($(this).attr("data-original")) {
        $(element).attr("href", $(this).attr("data-original"));
      }
      $(element).attr("data-fancybox", "images");
      var caption = "";   // 描述信息
      if ($(this).attr('alt')) {  // 标准 markdown 描述信息
        $(element).attr('data-caption', $(this).attr('alt'));
        caption = $(this).attr('alt');
      }
      var div = document.createElement("div");
      $(div).addClass("fancybox");
      $(this).wrap(div); // 最外层套 div ，其实主要作用还是 class 样式
      var span = document.createElement("span");
      $(span).addClass("image-caption");
      $(span).text(caption); // 加描述
      $(this).after(span);  // 再套一层描述
      $(this).wrap(element);  // 最后套 a 标签
    })
    $(".article-entry").find("img").fancybox({
      selector: '[data-fancybox="images"]',
      hash: false,
      loop: false,
      closeClick: true,
      helpers: {
        overlay: {closeClick: true}
      },
      buttons: [
        "zoom",
        "close"
      ]
    });
  };
  $(function () {
    pjax_fancybox();
  });
</script>




  <script>setLoadingBarProgress(100);</script>
</body>
</html>
